<?php
/******************************************************************************
 *
 *    OpenZoowa - Web App to manage events
 *    Copyright (C) 2008 Jesus Ruiz-Ayucar
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU Affero General Public License as
 *    published by the Free Software Foundation, either version 3 of the
 *    License, or (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *****************************************************************************/
   if (!defined('ZOOWA')) {
      include_once('../../config.php');
   }
   require_once(modelspath . 'tag.php');
   require_once(modelspath . 'city.php');
   require_once(modelspath . 'category.php');

   class EventException extends Exception { }

/**
 * Class to represent events
 *
 * @author Jesus Ruiz-Ayucar  <chuso@zoowa.net>
 * @version 0.1
 * @package openzoowa
 */
   class Event {
      const min_name = 3;
      const max_name = 90;
      const max_cost = 128;
      const min_description = 30;
      const max_description = 1048;
      const max_date_details = 128;
      const max_time = 128;
      const max_address = 128;
      const max_url = 256;
      const max_promoter = 128;
      const max_tags = 128;
      const max_phone = 13;
      const max_place = 128;
      const items = 4;

      private $id;
      private $name;
      private $name_clean;
      private $date_start;
      private $date_end;
      private $date_details;
      private $date_post;
      private $time;
      private $cost;
      private $address;
      private $place;
      private $description;
      private $url;
      private $phone;
      private $aforo;
      private $promoter;
      private $tags;
      private $category;
      private $user;
      private $city;
      private $error;
      private $comments;

      /**
      * Event constructor. Creates a new event.
      *
      * If no id is specified, the setup all fields to a standard value.
      * @param integer $id
      * @return Event
      */
      public function Event($id = null) {
         if (isset($id)) {
            if(!Util::is_numeric($id)) {
               throw new EventException('Not numeric ID.');
            }

            global $db;
            $SQL = 'SELECT * FROM '.$GLOBALS["db_pre"].'events ';
            $SQL .= 'WHERE id = '.$db->escape($id);
            $event = $db->get_row($SQL);

            if (count($event) != 1) {
               throw new EventException('Event not exists.');
            }
               $this->id = $event->id;
               $this->name = $event->name;
               $this->name_clean = $event->name_clean;
               $this->date_start = $event->date_start;
               $this->date_end = $event->date_end;
               $this->date_details = $event->date_details;
               $this->date_post = $event->date_post;
               $this->time = $event->time;
               $this->cost = $event->cost;
               $this->address = $event->address;
               $this->description = $event->description;
               $this->url = $event->url;
               $this->phone = $event->phone;
               $this->aforo = $event->aforo;
               $this->promoter = $event->promoter;
               $this->place = $event->place;
               $this->phone = $event->phone;

               $this->tags = new Tag($event->id);
               $this->category = new Category($event->category_id);
               $this->user = new User($event->user_id);
               $this->city = new City($event->city_id);
         } else {
            //Default values
            $this->id = null;
            $this->name = '';
            $this->name_clean = '';
            $this->date_start = '';
            $this->date_end = '';
            $this->date_details = '';
            $this->time = '';
            $this->cost = 0;
            $this->address = '';
            $this->description = '';
            $this->url = '';
            $this->phone = '';
            $this->aforo = -1;
            $this->promoter = '';
            $this->place = '';
            $this->phone = '';
            $this->tags = new Tag();
            $this->category = null;
            $this->user = null;
            $this->city = null;
         }
      }

      /**
      * Adds current event into database.
      *
      * @return integer greater than zero if success
      */
      public function add() {
         if (!$this->error) {
            global $db, $current_user;

            if($this->date_end == '' || $this->date_end == '0000-00-00') {
               $this->date_end = $this->date_start;
            }

            $SQL = 'INSERT INTO events (name, date_start, ';
            $SQL .= 'date_end, date_details, time, ';
            $SQL .= 'cost, address, city_id, place, ';
            $SQL .= 'category_id, user_id, description, ';
            $SQL .= 'url, phone, aforo, name_clean, promoter) VALUES (';

            $SQL .= '"' . $db->escape($this->name) .'", ';
            $SQL .= '"' . $db->escape($this->date_start) . '", ';
            $SQL .= '"' . $db->escape($this->date_end) . '", ';
            $SQL .= '"' . $db->escape($this->date_details) . '", ';
            $SQL .= '"' . $db->escape($this->time) . '", ';
            if (isset($this->cost)) {
               $SQL .= '"' . $db->escape($this->cost) . '", ';
            } else {
               $SQL .= 'null, ';
            }
            $SQL .= '"' . $db->escape($this->address) . '", ';
            $SQL .= $db->escape($this->city->getId()) . ', ';
            $SQL .= '"' . $db->escape($this->place) . '", ';
            $SQL .= $db->escape($this->category->getId()) . ', ';
            $SQL .= $this->user->getId() . ', ';
            $SQL .= '"' . $db->escape($this->description) . '", ';
            $SQL .= '"' . $db->escape($this->url) . '", ';
            $SQL .= '"' . $db->escape($this->phone) . '", ';
            $SQL .= $db->escape($this->aforo) . ', ';
            $SQL .= '"' . $db->escape($this->name_clean) .'", ';
            $SQL .= '"' . $db->escape($this->promoter) .'")';

            $result = $db->query($SQL);

            if ($result) {
               $this->id = $db->insert_id;

               // Añadimos tags
               $this->tags->setEventId($this->id);
               $this->tags->save();
            }
            return $this->id;
         }
      }

      /**
      * Updates current event into database.
      *
      * @return integer greater than zero if success
      */
      public function update() {
         if (!$this->error) {
            global $db;

            $SQL = 'UPDATE '.$GLOBALS["db_pre"].'events SET ';
            $SQL .= 'name = "' . $db->escape($this->name) . '", ';
            $SQL .= 'date_start = "' . $db->escape($this->date_start) . '", ';
            $SQL .= 'date_end = "' . $db->escape($this->date_end) . '", ';
            $SQL .= 'date_details = "' . $db->escape($this->date_details) . '", ';
            $SQL .= 'time = "' . $db->escape($this->time) . '", ';
            if(isset($this->cost)) {
               $SQL .= 'cost = "' . $db->escape($this->cost) . '",';
            } else {
               $SQL .= 'cost = null, ';
            }
            $SQL .= 'address = "' . $db->escape($this->address) . '", ';
            $SQL .= 'city_id = ' . $db->escape($this->city->getId()) . ',';
            $SQL .= 'category_id = ' . $db->escape($this->category->getId()) . ',';
            $SQL .= 'description = "' . $db->escape($this->description) . '", ';
            $SQL .= 'url = "' . $db->escape($this->url) . '", ';
            $SQL .= 'phone = "' . $db->escape($this->phone) . '", ';
            $SQL .= 'place = "' . $db->escape($this->place) . '", ';
            $SQL .= 'aforo = ' . $db->escape($this->aforo) . ', ';
            $SQL .= 'name_clean = "' . $db->escape($this->name_clean) . '", ';
            $SQL .= 'promoter = "' . $db->escape($this->promoter) . '" ';
            $SQL .= 'WHERE id = '.$this->id;

            // Añadimos tags
            $this->tags->setEventId($this->id);
            $this->tags->save();

            return $db->query($SQL);
         }
      }

      /**
      * Deletes current event from database.
      *
      * @return integer greater than zero if success
      */
      public function del() {
         global $db;
         $SQL  = 'DELETE FROM '.$GLOBALS["db_pre"].'events WHERE ';
         $SQL .= 'id = ' . $this->id;
         // Nota también hay que eliminar los tags
         return $db->query($SQL);
      }

      /**
      * Set the event name (Title)
      *
      * It checks some cosntraints such as name lenght and syntax
      * @param string $name
      * @return error msg or null if success
      */
      public function setName($name) {
         global $lang;
         $name = strip_tags(stripslashes($name));

         // Is it empty?
         if(empty($name)) {
            $error = true;
            return $lang['event_error_name1'];
         } elseif (strlen($name) < self::min_name || strlen($name) > self::max_name) {
            $error = true;
            return $lang['event_error_name3'];
         }  else {
            global $db;

            // We get the clean name to check if it already exists.
            $clean = Util::clean_name($db->escape(strip_tags($name)));
            $SQL = 'SELECT id FROM '.$GLOBALS["db_pre"].'events WHERE ';
            $SQL .= 'name_clean = "' . $db->escape($clean) . '"';
            $resul = $db->get_var($SQL);

            // If default clean name belongs to another event,
            // we should obtain a new one
            if ($resul != $this->id && !empty($resul)) {
               $suffix = 0;
               // We look for an available id. Condition: While event
               // exists AND belongs to another event
               do {
                  $suffix++;
                  $SQL  = 'SELECT id FROM '.$GLOBALS["db_pre"].'events WHERE ';
                  $SQL .= 'name_clean = "'.$clean.'-'.$suffix .'"';
                  $resul = $db->get_var($SQL);
               } while ($resul != $this->id && !empty($resul));

               $clean .= '-'.$suffix;
            }

            $this->name = strip_tags($name);
            $this->name_clean = $clean;
            return null;
         }
      }

      /**
      * Set the event category
      *
      * @param string $category
      * @return error msg or null if success
      */
      public function setCategory($category) {
         global $lang;
         $cat = new Category($category);
         $cat_id = $cat->getId();
         if(!isset($cat_id)) {
            $error = true;
            return $lang['event_error_category'];
         } else {
            $this->category = $cat;
            return null;
         }
      }

      /**
      * Set the event cost (price)
      *
      * @param integer $cost
      * @return error msg or null if success
      */
      public function setCost($cost) {
         global $lang;
         if($cost == -1) {
            $this->cost = null;
            return null;
         } else {
            if(strlen($cost) > self::max_cost) {
               $this->cost = null;
               $error = true;
               return $lang['event_error_cost'];
            } else {
               $this->cost = strip_tags($cost);
               return null;
            }
         }
      }

      /**
      * Set the event aforo (capacity)
      *
      * @param integer $num_aforo
      * @return error msg or null if success
      */
      public function setAforo($aforo) {
         global $lang;

         if(!Util::is_numeric($aforo)) {
            $error = true;
            return $lang['event_error_aforo'];
         } else {
            switch($aforo) {
               case -1:
                  // Unknown
                  $this->aforo = -1;
                  break;
               case 0:
                  // Unlimited
                  $this->aforo = 0;
                  break;
               default:
                  // Limited
                  if ($aforo) {
                     $this->aforo = $aforo;
                  } else {
                     $this->aforo = 0;
                  }
                  break;
            }
            return null;
         }
      }


      /**
      * Sets the event long description
      *
      * @param string $description
      * @return error msg or null if success
      */
      public function setDescription($description) {
         global $lang;

         $description = strip_tags(stripslashes($description),'<p><a><ul><ol><li><em><strong><br><img><object><param><embed>');

         if(strlen($description) > self::max_description || strlen($description) < self::min_description) {
            // Lo "guardamos" temporalmente
            $this->description = $description;
            $error = true;
            return $lang['event_error_description'];
         } else {
            $this->description = $description;
            return null;
         }
      }

      /**
      * Sets the publisher user id
      *
      * @param integer $id
      * @return error msg or null if success
      */
      public function setUser($id) {
         $this->user = new User($id);
         return null;
      }

      /**
      * Sets the event startup date
      *
      * @param integer $date_year
      * @param integer $date_month
      * @param integer $date_day
      * @return error msg or null if success
      */
      public function setDateStart($date_year, $date_month, $date_day) {
         global $lang;

         if(empty($date_year) ||
               empty($date_month) ||
               empty($date_day) ||
               !checkdate($date_month, $date_day, $date_year))
         {
            $error = true;
            return $lang['event_error_start_date1'];
         } else {
            $this->date_start = $date_year.'-'.$date_month.'-'.$date_day;
            return null;
         }
      }

      /**
      * Sets the event end date
      *
      * @param integer $date_year
      * @param integer $date_month
      * @param integer $date_day
      * @return error msg or null if success
      */
      public function setDateEnd($date_year, $date_month, $date_day) {
         global $lang;

         $ds = explode('-', $this->date_start);
         $ds = mktime(0,0,0,$ds[1], $ds[2], $ds[0]);
         if(empty($date_year) ||
               empty($date_month) ||
               empty($date_day) ||
               !checkdate($date_month, $date_day,$date_year))
         {
            $error = true;
            return $lang['event_error_end_date1'];
         } else if (mktime(0,0,0,$date_month, $date_day, $date_year) < $ds) {
            $error = true;
            return $lang['event_error_end_date2'];
         } else {
            $this->date_end = $date_year.'-'.$date_month.'-'.$date_day;
            return null;
         }
      }

      /**
      * Sets the details of the date (If needed)
      *
      * @param string $date_details
      * @return error msg or null if success
      */
      public function setDateDetails($date_details) {
         global $lang;

         if(strlen($date_details) > self::max_date_details) {
            $error = true;
            return $lang['event_error_date_details'];
         } else {
            $this->date_details = strip_tags($date_details);
            return null;
         }
      }

      public function setTime($time) {
         global $lang;

         if(strlen($time) > self::max_time || empty($time)) {
            $error = true;
            return $lang['event_error_time'];
         } else {
            $this->time =  strip_tags($time);
            return null;
         }
      }

      /**
      * Sets the municipality
      *
      * @param integer $city_id City id
      * @return error msg or null if success
      */
      public function setCity($city_id) {
         global $lang;
         $city = new City($city_id);
         $city_id = $city->getId();

         if(!isset($city_id)) {
            $error = true;
            return $lang['event_error_municipality'];
         } else {
            $this->city = $city;
         }
      }

      /**

      * Sets the address
      *
      * @param string $address
      * @return error msg or null if success
      */
      public function setAddress($address) {
         global $lang;

         $address = strip_tags(stripslashes($address));

         if(strlen($address) > self::max_address) {
            $error = true;
            return $lang['event_error_address'];
         } else {
            $this->address =  strip_tags($address);
            return null;
         }
      }

      /**
      * Sets the url
      *
      * @param string $url in http://www.example.com/page format
      * @return error msg or null if success
      */
      public function setUrl($url) {
         global $lang;

         if (!empty($url) && !eregi('^http:\/\/(.*)$', $url, $res)) {
            $url = 'http://' . $url;
         }

         $url = strip_tags(stripslashes($url));

         // Checking url
         if(!empty($url) && !Util::validateUrl($url)) {
            $error = true;
            return $lang['event_error_url'];
         } elseif (strlen($url) > self::max_url) {
            $error = true;
            return $lang['event_error_url_2'];
         } else {
            $this->url =  strip_tags($url);
            return null;
         }
      }

      /**
      * Sets the promoter
      *
      * @param string $promoter
      * @return error msg or null if success
      */
      public function setPromoter($promoter) {
         global $lang;

         $promoter = strip_tags(stripslashes($promoter));

         if(empty($promoter) || strlen($promoter) > self::max_promoter) {
            $error = true;
            return $lang['event_error_promoter'];
         } else {
            $this->promoter =  strip_tags($promoter);
            return null;
         }
      }

      /**
      * Sets the tags
      *
      * @param string $tags in "tag1, tag2, ..., tagn" format
      * @return error msg or null if success
      */
      public function setTags($tags) {
         global $lang;

         if(strlen($tags) > self::max_tags) {
            $error = true;
            return $lang['event_error_tags'];
         } else {
            $this->tags->tags = strip_tags($tags);
            return null;
         }
      }

      /**
      * Sets the contact phone number
      *
      * @param string $phone
      * @return error msg or null if success
      */
      public function setPhone($phone) {
         global $lang;

         if(strlen($phone) > self::max_phone) {
            $error = true;
            return $lang['event_error_phone'];
         } else {
            $this->phone = strip_tags($phone);
            return null;
         }
      }

      /**
      * Sets the place name
      *
      * @param string $place
      * @return error msg or null if success
      */
      public function setPlace($place) {
         global $lang;

         if(strlen($place) > self::max_place) {
            $error = true;
            return $lang['event_error_place'];
         } else {
            $this->place =  strip_tags($place);
            return null;
         }
      }


      public function getId() {
         return $this->id;
      }

      public function getName() {
         return $this->name;
      }

      public function getNameClean() {
         return $this->name_clean;
      }

      public function getDateStart() {
         return Util::format_date($this->date_start);
      }

      public function getDateStartTimestamp() {
         $date = explode('-', $this->date_start);
         return mktime(0,0,0,$date[1],$date[2],$date[0]);
      }

      public function getDateEnd() {
         return Util::format_date($this->date_end);
      }

      public function getDateEndTimestamp() {
         $date = explode('-', $this->date_end);
         return mktime(0,0,0,$date[1],$date[2],$date[0]);
      }

      public function getDateDetails() {
         return $this->date_details;
      }

      public function getDatePost() {
         return $this->date_post;
      }

      public function getDatePostTimestamp() {
         $date = explode('-', $this->date_post);
         return mktime(0,0,0,$date[1],$date[2],$date[0]);
      }

      public function getTime() {
         return $this->time;
      }

      public function getCost() {
         return $this->cost;
      }

      public function getAddress() {
         return $this->address;
      }

      public function getPlace() {
         return $this->place;
      }

      public function getDescription() {
         return $this->description;
      }

      public function getUrl() {
         return $this->url;
      }

      public function getPhone() {
         return $this->phone;
      }

      public function getAforo() {
         return $this->aforo;
      }

      public function getPromoter() {
         return $this->promoter;
      }

      public function getTags() {
         return $this->tags;
      }

      public function getCategory() {
         return $this->category;
      }

      public function getUser() {
         return $this->user;
      }

      public function getCity() {
         return $this->city;
      }

      public function getComments() {
         require_once(modelspath.'comment.php');
         if(!is_array($this->comments)) {
            $this->comments = Comment::getCommentsFromEventId($this->id);
         }
         return $this->comments;
      }

      public function getNumComments() {
         return count($this->getComments());
      }



/**
 *    HELPERS
 */

      /**
      * Returns an standard list of highlighted events. If $page is null, then
      * the number of total pages returns 
      *
      * @param integer $page Current page.
      * @param integer $region Region id
      * @param integer $cat Category id
      * @param string $date Date for the event in YYYY-MM-DD format
      * @param string $date_end End date for the event in YYYY-MM-DD format
      * @return array of Event
      */
      public static function getList(
            $page = null,
            $region = 0,
            $date = null,
            $date_end = null) {
         global $db;

         if(isset($date) && !isset($date_end)) {
            $date_end = $date;
         }

         if(isset($page)) {
            $SQL = 'SELECT e.id ';
         } else {
            $SQL = 'SELECT count(*) ';
         }

         $SQL .= ' FROM '.$GLOBALS["db_pre"].'events e ';

         if(isset($region) && $region != 0) {
            $SQL .= ',cities c ';
         }

         $SQL .= ' WHERE (1=1) ';
         if(isset($region) && $region != 0) {
            $SQL .= ' AND e.city_id = c.id ';
            $SQL .= ' AND c.region_id = '. $db->escape($region);
         }

         if (isset($date)) {
            $SQL .= ' AND \'' . $db->escape($date_end) . '\' >= e.date_start ';
            $SQL .= ' AND \'' . $db->escape($date) . '\' <= e.date_end ';
         } else {
            $SQL .= ' AND e.date_end >= "' . date('Y-m-d') .'"';
         }

         if(isset($page)) {
            $SQL .= ' ORDER BY date_start, date_end, date_post ';
            $SQL .= ' LIMIT ' . ($db->escape($page)*self::items) . ', ' . self::items;

            $result = $db->get_results($SQL);
            if(isset($result)) {
               foreach($result as $event) {
                  $list[] = new Event($event->id);
               }
            }
            return $list;
         } else {
            return (int)(($db->get_var($SQL) / self::items) + 1);
         }
      }

      public static function search($page = null, $string) {
         global $db;

         if(isset($page)) {
            $SQL = 'SELECT * ';
         } else {
            $SQL = 'SELECT count(*) ';
         }

         $SQL .= 'FROM '.$GLOBALS["db_pre"].'events ';
         $SQL .= 'WHERE id IN (';
         $SQL .= ' SELECT id FROM '.$GLOBALS["db_pre"].'events ';
         $SQL .= ' WHERE MATCH(name, description, promoter) ';
         $SQL .= ' AGAINST ("' . $db->escape($string) . '") ';
         $SQL .= ') OR id IN (';
         $SQL .= ' SELECT event_id FROM '.$GLOBALS["db_pre"].'tags ';
         $SQL .= ' WHERE MATCH(tag) ';
         $SQL .= ' AGAINST ("' . $db->escape($string) . '") ';
         $SQL .= ')';

         if(isset($page)) {
            $SQL .= ' ORDER BY date_start, date_end, date_post desc ';
            $SQL .= ' LIMIT ' . ($db->escape($page)*self::items) . ', ' . self::items;

            $result = $db->get_results($SQL);
            if (!empty($result)) {
               foreach($result as $event) {
                  $list[] = new Event($event->id);
               }
            }
            return $list;
         } else {
            return (int)(($db->get_var($SQL) / self::items) + 1);
         }
      }

      /**
      * Returns the event if from a clean name
      *
      * @param string $clean
      * @return integer
      */
      public static function getIdFromClean($clean) {
         global $db;
         $SQL =  'SELECT id FROM '.$GLOBALS["db_pre"].'events ';
         $SQL .= 'WHERE name_clean = "' . $db->escape($clean) . '"';
         return $db->get_var($SQL);
      }
   }
?>